package com.hengxinyongli.iam.web.filter;

import com.alibaba.fastjson.JSON;
import com.google.common.collect.Maps;
import com.hengxinyongli.iam.anotation.NoAuth;
import com.hengxinyongli.iam.anotation.ValidateToken;
import com.hengxinyongli.iam.dto.Subject;
import com.hengxinyongli.iam.exception.IamServiceException;
import com.hengxinyongli.iam.exception.IamTokenVerifyException;
import com.hengxinyongli.iam.remote.IamService;
import com.hengxinyongli.iam.web.constant.Res;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.AutoConfigurationPackages;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Component;
import org.springframework.util.Assert;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerExecutionChain;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;

import javax.annotation.PostConstruct;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.List;
import java.util.Map;
import java.util.Objects;

/**
 * @author magic
 * @date 2018/10/15/015 19:34
 * @version 1.0
 * Description IamRequestMethodHandler
 */
public class RequestAssignment {

    @Autowired
    List<RequestMappingHandlerMapping> handlerMappings;
    @Autowired
    BeanFactory beanFactory;

    private Logger LOG = LoggerFactory.getLogger(RequestAssignment.class);
    private String sourcePackage;

    @PostConstruct
    public void init(){
        sourcePackage = AutoConfigurationPackages.get(beanFactory).get(0);
    }

    public  HandlerMethod fetchHandlerMethod(HttpServletRequest request) {
        HandlerMethod handlerMethod = null;
        try {
            for (RequestMappingHandlerMapping handler : handlerMappings) {
                HandlerExecutionChain executionChain = handler.getHandler(request);
                if(null != executionChain && executionChain.getHandler() instanceof HandlerMethod){
                    handlerMethod = (HandlerMethod) executionChain.getHandler();
                    break;
                }
            }
        }catch (Exception e){
            LOG.warn("findHandlerChain exception: {} ", ExceptionUtils.getRootCauseMessage(e));
        }

        if(null != handlerMethod
                && handlerMethod.getBeanType().getName().startsWith(sourcePackage)){
            return handlerMethod;
        }else{
            return null;
        }
    }


    private boolean checkJustValidateToken(HandlerMethod handlerMethod) {
        if(Objects.isNull(handlerMethod)){
            return true;
        }
        ValidateToken validateToken = handlerMethod.getMethodAnnotation(ValidateToken.class);
        if (Objects.nonNull(validateToken)){
            return true;
        }
        return false;
    }

    public  boolean checkNoAuthHandlerMethod(HandlerMethod handlerMethod) {
        if(Objects.isNull(handlerMethod)){
            return true;
        }
        NoAuth noAuth = handlerMethod.getMethodAnnotation(NoAuth.class);
        return null != noAuth;
    }

    public  void writeErrorMsg(HttpServletResponse response, String message) {
        response.setStatus(HttpServletResponse.SC_OK);
        response.setContentType(MediaType.APPLICATION_JSON_UTF8_VALUE);
        Map<String,Object> errorMsg = Maps.newHashMap();
        errorMsg.put(Res.ERROR_MESSAGE,message);
        errorMsg.put(Res.ERROR_CODE_KEY ,Res.ERROR_CODE);
        try {
            PrintWriter printWriter = response.getWriter();
            printWriter.write(JSON.toJSONString(errorMsg));
            printWriter.flush();
            printWriter.close();
        } catch (IOException e) {
            LOG.error("writeErrorMsg error: {}", ExceptionUtils.getRootCauseMessage(e));
        }
    }

    public void setSubjectAttributes(HttpServletRequest request, Subject subject) {
        request.setAttribute(Res.USER_ID,subject.getUserId());
        request.setAttribute(Res.USER_NAME,subject.getUserName());
        request.setAttribute(Res.USER_MOBILE,subject.getMobile());
        request.setAttribute(Res.USER_TYPE,subject.getUserType());
        request.setAttribute(Res.USER_DEPARTMENT_ID,subject.getDepId());
        request.setAttribute(Res.USER_DEPARTMENT_CODE,subject.getDepLabel());
        request.setAttribute(Res.USER_SUBJECT,subject);
    }

    public void doFilter(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain, IamService iamService)  throws ServletException, IOException {
        HandlerMethod handlerMethod = fetchHandlerMethod(request);
        if(checkNoAuthHandlerMethod(handlerMethod)){
            filterChain.doFilter(request,response);
            return;
        }
        Boolean justValidateToken = checkJustValidateToken(handlerMethod);
        String userToken = request.getHeader(Res.USER_TOKEN);
        String uri = request.getRequestURI();
        String method = request.getMethod();
        try {
            Assert.hasText(userToken,"HttpHeader UserToken not allow empty.");
            Subject subject = iamService.validateToken(userToken,uri,method,justValidateToken);
            LOG.info("IAM Validate Token Subject,UserId: {} ",subject.getUserId());
            setSubjectAttributes(request,subject);
            filterChain.doFilter(request,response);
        }catch (IamTokenVerifyException | IamServiceException exp){
            LOG.warn("IIamTokenVerifyException  {} ",ExceptionUtils.getRootCauseMessage(exp));
            writeErrorMsg(response,exp.getMessage());
        }
    }

}
